package com.guardian.monitor;

import com.guardian.buffer.GuardianObjectBuffer;
import com.guardian.buffer.MonitorBuffer;
import com.guardian.email.MailUtil;
import com.guardian.entity.GuardianObject;
import com.guardian.util.ProcessUtils;

import javax.mail.MessagingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 应用监听器
 * @author: huwei
 * @date: 2019/10/11 13:37
 * @version: 1.0.0
 */
public class GuardianObjectMonitor {

    /**
     * 默认休眠时间10s
     */
    private static final long DEFAULT_IDLE_TIME = 10 * 1000;

    /**
     * 守护对象
     */
    private static Map<String , GuardianObject> guardianObjectMap = GuardianObjectBuffer.copyBufferAndResetChange();

    /**
     * 监视器错误次数
     */
    private static AtomicInteger errorCount = new AtomicInteger(0);

    /**
     * 异步启动，并监视守护对象
     */
    public void asyncMonitor(){
        new Thread(()->{
            syncMonitor();
        }).start();
    }

    public void syncMonitor(){
        while(true){
            try {
                //检测守护对象更新
                if (GuardianObjectBuffer.isChanged()) {
                    updateGuardianObject();
                }

                //检测守护对象
                guardianObjectMonitor();

                //休眠
                Thread.sleep(getIdleTime());

            } catch (Throwable e){
                e.printStackTrace();

                errorCount.incrementAndGet();

                //TODO 异常处理
                if(errorCount.get() >= 10){
                    System.out.println("guardian system error ，error count:".concat(String.valueOf(errorCount.get())));
                }
            }
        }
    }

    private long getIdleTime(){
        Object idleTime = MonitorBuffer.get(MonitorBuffer.MonitorBufferName.IDLE_TIME);
        if(idleTime != null){
            return (long) idleTime;
        }
        return DEFAULT_IDLE_TIME;
    }

    private void guardianObjectMonitor(){
        guardianObjectMap.forEach((guardianObjectName ,guardianObject)->{
            try {
                ProcessUtils.Result result = execOutCommand(guardianObject.getDetectionCommand());
                if (!guardianObjectName.equals(result.data)) {
                    throw new Exception("探查守护应用存活失败，返回参数错误：" + result.data);
                }
            }catch (Throwable e){
                try {
                    //重启应用
                    ProcessUtils.Result result = execOutCommand(guardianObject.getStartCommand());
                    if(result.code != 0){
                        throw new Exception("重启守护应用".concat(guardianObjectName).concat("失败，返回参数错误：") + result.code);
                    }
                    guardianObject.setStartSuccessCount(guardianObject.getStartSuccessCount() + 1);
                    guardianObject.setStartFailCount(0);

                    String subject = "重启守护应用 ".concat(guardianObjectName).concat(" 成功");
                    String message = subject.concat( ";次数 :").concat(String.valueOf(guardianObject.getStartSuccessCount()));
                    sendMailTo(subject ,message);
                }catch (Throwable e1){
                    //执行启动命令出现错误，记录启动次数
                    guardianObject.setStartFailCount(guardianObject.getStartFailCount() + 1);
                    //System.out.println("重启错误次数" + guardianObject.getStartFailCount());
                    if (guardianObject.getStartFailCount() >= 10) {
                        //移除监控
                        GuardianObjectBuffer.remove(guardianObjectName);
                    }
                    String subject = "重启守护应用 ".concat(guardianObjectName).concat(" 失败");
                    String message = subject.concat( ";次数 :").concat(String.valueOf(guardianObject.getStartFailCount()));
                    sendMailTo(subject ,message);
                }
            }

        });
    }

    private ProcessUtils.Result execOutCommand(String command){
        return ProcessUtils.run(command ,(String)MonitorBuffer.get("charset","GBK"));
    }


    private void updateGuardianObject(){
        guardianObjectMap = GuardianObjectBuffer.copyBufferAndResetChange();
    }

    private void sendMailTo(String subject ,String message) {
        String host = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_SERVER_HOST);
        int port = getMailPort();
        String protocol = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_SERVER_PROTOCOL);
        String user = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_SERVER_USERNAME);
        String password = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_SERVER_PASSWORD);
        String from = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_FROM);

        List<String > tos = getMailTos();

        tos.forEach(x->{
            sendMailTo(host ,port ,protocol ,user ,password ,from ,x ,subject ,message);
        });
    }

    private void sendMailTo(String host ,int port ,String protocol ,String user ,String password ,String from ,String to ,String subject ,String message) {
        System.err.println("发送邮件,主题：".concat(subject).concat("; 内容：").concat(htmlMail(message)));
        try {
            MailUtil.build(host, port, protocol, user, password).send(from, to, subject, htmlMail(message));
        }catch (MessagingException e){
            System.err.println("发送邮件失败，主题：".concat(subject).concat("； 内容：").concat(htmlMail(message)));
        }
    }

    private String htmlMail(String message){
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("<span>");
        stringBuffer.append(message);
        stringBuffer.append("</span>");

        return stringBuffer.toString();
    }

    private List<String> getMailTos(){
        List<String> toList = Collections.EMPTY_LIST;
        String to = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_TO);
        if(to != null){
            String[] tos = to.split(" ");
            toList = new ArrayList<>(tos.length);
            for(String t : tos){
                toList.add(t);
            }
        }
        return toList;
    }

    private int getMailPort(){
        String portStr = (String) MonitorBuffer.get(MonitorBuffer.MonitorBufferName.MAIL_SERVER_PORT);
        try{
            return Integer.parseInt(portStr);
        } catch (Exception e){
            //ignore
        }
        return 465;
    }
}
